using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace GameModels
{
    public enum CameraType
    {
        FirstPerson, ThirdPerson, FreeCamera
    }
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    /// 
    public class Camera : Microsoft.Xna.Framework.GameComponent
    {
        public Camera(Game game)
            : base(game)
        {
            // TODO: Construct any child components here
            this.CameraType = CameraType.ThirdPerson;
            this.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, Game.GraphicsDevice.Viewport.AspectRatio, 10, 10000);
            this.Yaw = 0;
            this.Pitch = 0;
            this.Roll = 0;
            this.Distance = 500;
            this.Position = new Vector3(0, 100, 0);
            this.Game.Components.Add(this);
        }
        private MouseState lastMouse;
        public CameraType CameraType { get; set; }//Type of camera
        public Matrix View { get; set; }//View matrix
        public Matrix Projection { get; set; }//Projection matrix
        public Vector3 Position { get; set; }//Position of camera
        public float Yaw { get; set; }//Yaw angle
        public float Pitch { get; set; }//Pitch angle
        public float Roll { get; set; }//Role angle
        public float Distance { get; set; }//Distance of camera
        public float Height { get; set; }
        private ModelBase _FocusedModel;
        private Vector3 _Up = Vector3.Up;
        private Vector3 _Right = Vector3.Zero;
        private Vector3 _Forward = Vector3.Zero;
        public ModelBase FocusedModel
        {
            get { return _FocusedModel; }
            set
            {
                _FocusedModel = value;
                _FocusedModel.IsFocused = true;
                canmove = _FocusedModel.CanMove;
            }
        }// Camera focus on this model
        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            // TODO: Add your initialization code here

            base.Initialize();
        }
        public void CreateFirstPersonCamera()
        {
            if (FocusedModel == null) return;
            Position = FocusedModel.Position;
            Yaw = FocusedModel.Yaw;
            Pitch = FocusedModel.Picth;
            Roll = FocusedModel.Roll;
            _Forward = new Vector3(0, 0, Distance);
            _Forward = Vector3.Transform(_Forward, Matrix.CreateFromYawPitchRoll(Yaw, Pitch, Roll));
            Position = Position + FocusedModel.ModelEye;
            Vector3 targetLookAt = Position + _Forward;

            View = Matrix.CreateLookAt(Position, targetLookAt, _Up);

            _Forward.Normalize();
            _Right = Vector3.Cross(_Forward, _Up);
            _Right.Normalize();

        }
        public void CreateThirdPersonCamera()
        {
            if (FocusedModel == null) return;
            Vector3 targetLookAt = FocusedModel.Position;
            Yaw = FocusedModel.Yaw;
            _Forward = new Vector3(0, 0, Distance);
            _Forward = Vector3.Transform(_Forward, Matrix.CreateFromYawPitchRoll(Yaw, Pitch, Roll));
            Position = targetLookAt - _Forward;
            Position = Position + new Vector3(0, Height, 0);
            View = Matrix.CreateLookAt(Position, targetLookAt + FocusedModel.ModelEye, _Up);

            _Forward.Normalize();
            _Right = Vector3.Cross(_Forward, _Up);
            _Right.Normalize();

        }
        public void CreateFreeCamera()
        {
            MouseState currentMouse = Mouse.GetState();
            if (lastMouse != null)
            {
                if (currentMouse.X != lastMouse.X || currentMouse.Y != lastMouse.Y)
                {
                    Yaw = Yaw + (currentMouse.X - lastMouse.X) * 0.005f;
                    Pitch = Pitch + (currentMouse.Y - lastMouse.Y) * 0.005f;
                    Mouse.SetPosition(Game.GraphicsDevice.Viewport.Width / 2, Game.GraphicsDevice.Viewport.Height / 2);
                    lastMouse = Mouse.GetState();
                }
            }
            Vector3 targetLookAt = new Vector3(0, 0, Distance);
            targetLookAt = Vector3.Transform(targetLookAt, Matrix.CreateFromYawPitchRoll(Yaw, Pitch, Roll));
            KeyboardState state = Keyboard.GetState();
            Vector3 move = Vector3.Zero;
            float dx = 0, dz = 0;
            if (state.IsKeyDown(Keys.Down))
            {
                dz = -5;
            }
            if (state.IsKeyDown(Keys.Up))
            {
                dz = 5;
            }
            if (state.IsKeyDown(Keys.Left))
            {
                dx = -5;
            }
            if (state.IsKeyDown(Keys.Right))
            {
                dx = 5;
            }
            move = Vector3.Transform(new Vector3(dx, 0, dz), Matrix.CreateFromYawPitchRoll(Yaw, Pitch, Roll));
            Position = Position + move;

            targetLookAt = Position + targetLookAt;
            View = Matrix.CreateLookAt(Position, targetLookAt, _Up);

            _Forward = targetLookAt - Position;
            _Forward.Normalize();
            _Right = Vector3.Cross(_Forward, _Up);
            _Right.Normalize();

        }
        bool isKeyDown = false;
        bool canmove = false;
        public void CreateCamera()
        {
            KeyboardState keyBoard = Keyboard.GetState();
            if (keyBoard.IsKeyDown(Keys.H))
            {
                isKeyDown = true;
            }
            if (isKeyDown && keyBoard.IsKeyUp(Keys.H))
            {
                switch (this.CameraType)
                {
                    case GameModels.CameraType.FirstPerson:
                        this.CameraType = GameModels.CameraType.ThirdPerson;
                        if (FocusedModel != null)
                        {
                            FocusedModel.CanMove = canmove;
                        }
                        break;
                    case GameModels.CameraType.ThirdPerson:
                        this.CameraType = GameModels.CameraType.FreeCamera;
                        if (FocusedModel != null)
                        {
                            canmove = FocusedModel.CanMove;
                            FocusedModel.CanMove = false;
                        }
                        break;
                    case GameModels.CameraType.FreeCamera:
                        this.CameraType = GameModels.CameraType.FirstPerson;
                        if (FocusedModel != null)
                        {
                            FocusedModel.CanMove = canmove;
                        }
                        break;
                }
                isKeyDown = false;
            }
            if (this.CameraType == CameraType.FirstPerson)
            {
                CreateFirstPersonCamera();
            }
            else if (this.CameraType == CameraType.ThirdPerson)
            {
                CreateThirdPersonCamera();
            }
            else if (this.CameraType == CameraType.FreeCamera)
            {
                CreateFreeCamera();
            }
        }
        public void UpdateCameraToModel()
        {
            foreach (GameComponent comp in Game.Components)
            {
                if (comp is ModelBase)
                {
                    ModelBase model = (ModelBase)comp;
                    model.View = View;
                    model.Projection = Projection;
                }
            }
        }
        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here
            CreateCamera();
            UpdateCameraToModel();
            base.Update(gameTime);
        }

        public Ray GetCursorRay()
        {
            MouseState mouse = Mouse.GetState();
            return GetCursorRay(new Vector2(mouse.X, mouse.Y));
        }
        public Ray GetCursorRay(Vector2 cursorPosition)
        {
            // create 2 positions in screenspace using the cursor position. 0 is as
            // close as possible to the camera, 1 is as far away as possible.

            Vector3 nearSource = new Vector3(cursorPosition, 0f);
            Vector3 farSource = new Vector3(cursorPosition, 1f);

            // use Viewport.Unproject to tell what those two screen space positions
            // would be in world space. we'll need the projection matrix and view
            // matrix, which we have saved as member variables. We also need a world
            // matrix, which can just be identity.
            Vector3 nearPoint = Game.GraphicsDevice.Viewport.Unproject(nearSource,
                Projection, View, Matrix.Identity);

            Vector3 farPoint = Game.GraphicsDevice.Viewport.Unproject(farSource,
                Projection, View, Matrix.Identity);

            // find the direction vector that goes from the nearPoint to the farPoint
            // and normalize it....
            Vector3 direction = farPoint - nearPoint;
            direction.Normalize();
            // and then create a new ray using nearPoint as the source.
            return new Ray(nearPoint, direction);
        }
    }
}
